package com.sl.core.job;

import cn.hutool.core.date.DateUtil;
import com.sl.core.job.enums.JobStateEnum;
import com.sl.core.job.scheduler.JobTask;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;

import java.util.Date;
import java.util.Properties;
import java.util.function.Function;

//@RequiredArgsConstructor
//@Configuration
@Slf4j
public class ProcessJobConfig {

    public static ProcessJobConfig INSTANCE = new ProcessJobConfig();

    public static final String SCHEDULE_NAME = "BOOT_JOB_";

    public Scheduler scheduler = null;

    public void init(DataSourceProperties dataSourceProperties, Function<String,Object> callBack) {
        scheduler = scheduler(dataSourceProperties);
        JobTask.setCallBack(callBack);
    }

    @SneakyThrows
    public Scheduler scheduler(DataSourceProperties dataSourceProperties) {

        Properties properties = new Properties();
        //设置调度器的实例名(instanceName) 和实例ID(instanceId)
        //注意：如果使用JobStoreTX，实例名严禁使用：DefaultQuartzScheduler
        //原因：内存方式的instanceid为默认的DefaultQuartzScheduler，如果不修改系统会同时存在内存型和DB型，默认会走内存
//        properties.put("org.quartz.jdbc.initialize-schema", "false");

        properties.put("org.quartz.scheduler.instanceName", "MyQuartzScheduler");
        //如果使用集群，instanceId必须唯一，设置成AUTO
        properties.put("org.quartz.scheduler.instanceId", "AUTO");
        properties.put("org.quartz.scheduler.rmi.export", "false");
        properties.put("org.quartz.scheduler.rmi.proxy", "false");
        properties.put("org.quartz.scheduler.wrapJobExecutionInUserTransaction", "false");

        properties.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        properties.put("org.quartz.threadPool.threadCount", "10");
        properties.put("org.quartz.threadPool.threadPriority", "5");
        properties.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
        properties.put("org.quartz.jobStore.misfireThreshold", "60000");

        properties.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        properties.put("org.quartz.jobStore.dataSource", "sl");

        //驱动器方言
        properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        //是否使用集群（如果项目只部署到 一台服务器，就不用了）
        properties.put("org.quartz.jobStore.dontSetAutoCommitFalse", "false");
        properties.put("org.quartz.jobStore.isClustered", "false");

//        properties.put("org.quartz.jobStore.useProperties", "true");
        properties.put("org.quartz.jobStore.tablePrefix", "SL_");
        properties.put("org.quartz.dataSource.sl.driver", dataSourceProperties.getDriverClassName());
        properties.put("org.quartz.dataSource.sl.URL", dataSourceProperties.getUrl());
        properties.put("org.quartz.dataSource.sl.user", dataSourceProperties.getUsername());
        properties.put("org.quartz.dataSource.sl.password", dataSourceProperties.getPassword());
//        properties.put("org.quartz.dataSource.sl.name", dataSourceProperties.getName());
//        properties.put("org.quartz.dataSource.myTest.maxConnection", "10");
        properties.put("org.quartz.dataSource.sl.validateOnCheckout", "true");
        properties.put("org.quartz.dataSource.sl.validationQuery", "select 1");


        StdSchedulerFactory fact = new StdSchedulerFactory();
        fact.initialize(properties);
        Scheduler scheduler = fact.getScheduler();
        scheduler.start();
        return scheduler;
    }

    @SneakyThrows
    public void addJob(String jobId, String jobParameter,
                       String cron, String state) {
        // 构建job信息
        JobDetail jobDetail = JobBuilder.newJob(JobTask.class).withIdentity(getJobKey(jobId)).build();

        jobDetail.getJobDataMap().put("parameter", jobParameter);

        // 表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing();

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId))
                .startAt(DateUtil.date()).withSchedule(scheduleBuilder).build();

        scheduler.scheduleJob(jobDetail, trigger);

        checkStop(jobId, state);
//        scheduler.getjob
    }


    /**
     * 定时任务 Key
     */
    public JobKey getJobKey(String jobId) {
        return JobKey.jobKey(SCHEDULE_NAME + jobId);
    }


    public void updateJob(String jobId, String cron, String params, String state) {
        try {
            // 查询触发器Key
            TriggerKey triggerKey = getTriggerKey(jobId);
            // 构建Cron调度器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                    .cronSchedule(cron)
                    .withMisfireHandlingInstructionDoNothing();
            // 任务触发器
            CronTrigger trigger = getCronTrigger(jobId)
                    .getTriggerBuilder().startAt(DateUtil.date()).withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder).build();
            trigger.getJobDataMap().put("parameter", params);
            scheduler.rescheduleJob(triggerKey, trigger);
            // 状态校验
            checkStop(jobId, state);
        } catch (SchedulerException e) {
            throw new RuntimeException("updateJob Fail", e);
        }
    }


    /**
     * 表达式触发器
     */
    public CronTrigger getCronTrigger(String jobId) {
        try {
            return (CronTrigger) this.scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("getCronTrigger Fail", e);
        }
    }

    /**
     * 触发器 KEY
     */
    public TriggerKey getTriggerKey(String jobId) {
        return TriggerKey.triggerKey(SCHEDULE_NAME + jobId);
    }


    public void checkStop(String id, String state) {
        try {
            if (!state.equals(JobStateEnum.JOB_RUN.getStatus())) {
                this.scheduler.pauseJob(getJobKey(id));
            }
        } catch (SchedulerException e) {
            throw new RuntimeException("pauseJob Fail", e);
        }
    }

    public void resumeJob(String id) {
        try {
            this.scheduler.resumeJob(getJobKey(id));
        } catch (SchedulerException e) {
            throw new RuntimeException("resumeJob Fail", e);
        }
    }

    public void run(String id, String params) {
        try {
            // 查询触发器Key
            TriggerKey triggerKey = getTriggerKey(id);

            // 任务触发器
            CronTrigger trigger = getCronTrigger(id);
            trigger = trigger.getTriggerBuilder().startNow().build();
            trigger.getJobDataMap().put("parameter", params);
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            throw new RuntimeException("updateJob Fail", e);
        }
    }

    public void runOne(String id, String params) {
        try {
            JobDataMap dataMap = new JobDataMap();
            dataMap.put("parameter", params);
            this.scheduler.triggerJob(getJobKey(id), dataMap);
        } catch (SchedulerException e) {
            throw new RuntimeException("run Fail", e);
        }
    }


    @SneakyThrows
    public void remove(String id) {
        JobKey jobKey = getJobKey(id);

        try {
            this.scheduler.pauseJob(jobKey);
        } catch (Exception e) {
            log.error("停止job失败");
        }
        this.scheduler.deleteJob(jobKey);
    }

    @SneakyThrows
    public Date nextFireTime(String id) {
        Trigger trigger = this.scheduler.getTrigger(getTriggerKey(id));
        Date nextFireTime = trigger.getNextFireTime();
        return nextFireTime;
    }
}
